package wyp.library.tools;

import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;

/**
 * 经典的数字签名算法RSA 数字签名
 * 
 * @author kp 本例需引用 org.bouncycastle.jar
 * */
public class RSAUtil {
	/**
	 * 数字签名---密钥算法
	 */
	public static final String KEY_ALGORITHM = "RSA";
	/**
	 * 数字签名------签名/验证算法
	 * */
	public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
	/**
	 * RSA密钥长度，RSA算法的默认密钥长度是1024 密钥长度必须是64的倍数，在512到65536位之间
	 * */
	private static final int KEY_SIZE = 512;
	/**
	 * 公钥
	 */
	private static final String PUBLIC_KEY = "RSAPublicKey";
	/**
	 * 私钥
	 */
	private static final String PRIVATE_KEY = "RSAPrivateKey";

	/**
	 * 初始化密钥对
	 * 
	 * @return Map 甲方密钥的Map
	 * */
	public static Map<String, Object> initKey() throws Exception {
		// 实例化密钥生成器
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		// 初始化密钥生成器
		keyPairGenerator.initialize(KEY_SIZE);
		// 生成密钥对
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		// 甲方公钥
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		// 甲方私钥
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		// 将密钥存储在map中
		Map<String, Object> keyMap = new HashMap<String, Object>();
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;

	}

	/**
	 * 取得私钥
	 * 
	 * @param keyMap
	 *            密钥map
	 * @return Key 私钥
	 * */
	public static RSAPrivateKey getPrivateKey(Map<String, Object> keyMap) throws Exception {
		// // 获取私钥
		RSAPrivateKey priKey = (RSAPrivateKey) keyMap.get(PRIVATE_KEY);
		// 返回私钥系数(字节数组形式)
		byte[] priModBytes = priKey.getModulus().toByteArray();
		// 返回私钥专用指数(字节数组形式)
		byte[] priPriExpBytes = priKey.getPrivateExponent().toByteArray();

		KeyFactory keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
		RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(priModBytes), new BigInteger(priPriExpBytes));
		// 生成私钥
		RSAPrivateKey recoveryPriKey = (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
		return recoveryPriKey;

		// return priKey;
	}

	/**
	 * 取得公钥
	 * 
	 * @param keyMap
	 *            密钥map
	 * @return Key 公钥
	 * */
	public static RSAPublicKey getPublicKey(Map<String, Object> keyMap) throws Exception {
		// 获取公钥
		RSAPublicKey pubKey = (RSAPublicKey) keyMap.get(PUBLIC_KEY);
		// 获取公钥系数(字节数组形式)
		byte[] pubModBytes = pubKey.getModulus().toByteArray();
		// 返回公钥公用指数(字节数组形式)
		byte[] pubPubExpBytes = pubKey.getPublicExponent().toByteArray();

		// 生成公钥
		KeyFactory keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
		RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(pubModBytes), new BigInteger(pubPubExpBytes));
		RSAPublicKey recoveryPubKey = (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
		return recoveryPubKey;

		// return pubKey;
	}

	/**
	 * 私钥签名
	 * 
	 * @param data
	 *            待签名数据
	 * @param privateKey
	 *            私钥
	 * @return byte[] 数字签名
	 * */
	public static byte[] sign(byte[] data, Key key) throws Exception {

		// // 取得私钥
		// PKCS8EncodedKeySpec pkcs8KeySpec = new
		// PKCS8EncodedKeySpec(key.getEncoded());
		// KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		// // 生成私钥
		// PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
		// 实例化Signature
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		// 初始化Signature
		// signature.initSign(priKey);
		signature.initSign((PrivateKey) key);
		// 更新
		signature.update(data);
		return signature.sign();
	}

	/**
	 * 校验数字签名
	 * 
	 * @param data
	 *            待校验数据
	 * @param publicKey
	 *            公钥
	 * @param sign
	 *            数字签名
	 * @return boolean 校验成功返回true，失败返回false
	 * */
	public static boolean verify(byte[] data, Key key, byte[] sign) throws Exception {
		// // 转换公钥材料
		// // 实例化密钥工厂
		// KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		// // 初始化公钥
		// // 密钥材料转换
		// X509EncodedKeySpec x509KeySpec = new
		// X509EncodedKeySpec(key.getEncoded());
		// // 产生公钥
		// PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
		// 实例化Signature
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		// 初始化Signature
		// signature.initVerify(pubKey);
		signature.initVerify((PublicKey) key);
		// 更新
		signature.update(data);
		// 验证
		return signature.verify(sign);
	}

	/**
	 * 加密
	 * 
	 * @param key
	 *            加密的密钥
	 * @param data
	 *            待加密的明文数据
	 * @return 加密后的数据
	 * @throws Exception
	 */
	public static byte[] encrypt(Key key, byte[] data) throws Exception {
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM, new org.bouncycastle.jce.provider.BouncyCastleProvider());
		cipher.init(Cipher.ENCRYPT_MODE, key);
		// 获得加密块大小，如:加密前数据为128个byte，而key_size=1024 加密块大小为127
		// byte,加密后为128个byte;
		// 因此共有2个加密块，第一个127 byte第二个为1个byte
		int blockSize = cipher.getBlockSize();
		int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
		int leavedSize = data.length % blockSize;
		int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
		byte[] raw = new byte[outputSize * blocksSize];
		int i = 0;
		while (data.length - i * blockSize > 0) {
			if (data.length - i * blockSize > blockSize)
				cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
			else
				cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
			// 这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到ByteArrayOutputStream中
			// ，而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了OutputSize所以只好用dofinal方法。
			i++;
		}
		return raw;
	}

	/**
	 * 解密
	 * 
	 * @param key
	 *            解密的密钥
	 * @param raw
	 *            已经加密的数据
	 * @return 解密后的明文
	 * @throws Exception
	 */
	public static byte[] decrypt(Key key, byte[] raw) throws Exception {
		Cipher cipher = Cipher.getInstance(KEY_ALGORITHM, new org.bouncycastle.jce.provider.BouncyCastleProvider());
		cipher.init(cipher.DECRYPT_MODE, key);
		int blockSize = cipher.getBlockSize();
		ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
		int j = 0;
		while (raw.length - j * blockSize > 0) {
			bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
			j++;
		}
		return bout.toByteArray();
	}

	/**
	 * 私钥解密 传入加密的数据 直接得到明文
	 * 
	 * @param str
	 * @return prieKey String类型的私钥
	 * @throws Exception
	 */
	public static String getRightData(String str, String prieKey) throws Exception {
		// 传入的私钥被BASE64加过密
		byte[] keyBytes = Base64Util.decryptBASE64(prieKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
		String resultStr = new String(RSAUtil.decrypt(privateKey, Base64Util.decryptBASE64(str)),"gbk");
		return resultStr;
	}

	/**
	 * 加密<br>
	 * 用私钥加密
	 * 
	 * @param data
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static String encrypt(byte[] data, String key) throws Exception {
		// 对密钥解密
		byte[] keyBytes = Base64Util.decryptBASE64(key);

		// 取得私钥
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);

		// 对数据加密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);

		return Base64Util.encryptBASE64(cipher.doFinal(data));
	}

	
	/**
	 * 用私钥对信息生成数字签名
	 * 
	 * @param data
	 *            加密数据
	 * @param privateKey
	 *            私钥
	 * 
	 * @return
	 * @throws Exception
	 */
	public static String sign2(byte[] data, String privateKey) throws Exception {
		// 解密由base64编码的私钥
		byte[] keyBytes = Base64Util.decryptBASE64(privateKey);

		// 构造PKCS8EncodedKeySpec对象
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

		// KEY_ALGORITHM 指定的加密算法
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

		// 取私钥匙对象
		PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

		// 用私钥对信息生成数字签名
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(priKey);
		signature.update(data);

		return Base64Util.encryptBASE64(signature.sign());
	}

	/**
	 * 校验数字签名
	 * 
	 * @param data
	 *            加密数据
	 * @param publicKey
	 *            公钥
	 * @param sign
	 *            数字签名
	 * @return 校验成功返回true 失败返回false
	 * @throws Exception
	 * 
	 */
	public static boolean verify2(byte[] data, String publicKey, String sign)
			throws Exception {

		// 解密由base64编码的公钥
		byte[] keyBytes = Base64Util.decryptBASE64(publicKey);

		// 构造X509EncodedKeySpec对象
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

		// KEY_ALGORITHM 指定的加密算法
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

		// 取公钥匙对象
		PublicKey pubKey = keyFactory.generatePublic(keySpec);

		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initVerify(pubKey);
		signature.update(data);

		// 验证签名是否正常
		return signature.verify(Base64Util.decryptBASE64(sign));
	}
}
